
class WebSocketWrapper {
    private url: string;
    private socket: WebSocket | null = null;
    private reconnectAttempts: number = 0;
    private maxReconnectAttempts: number;
    private reconnectDelay: number;
    private heartbeatInterval: number;
    private heartbeatTimer: NodeJS.Timeout | null = null;
    private reconnectTimer: NodeJS.Timeout | null = null;
    private eventListeners: Record<string, Function[]> = {};
    private messageQueue: any[] = [];
    private isConnected: boolean = false;

    constructor(
        url: string,
        options: {
            maxReconnectAttempts?: number;
            reconnectDelay?: number;
            heartbeatInterval?: number;
        } = {}
    ) {
        this.url = url;
        this.maxReconnectAttempts = options.maxReconnectAttempts || 5;
        this.reconnectDelay = options.reconnectDelay || 5000;
        this.heartbeatInterval = options.heartbeatInterval || 30000;
        this.connect();
    }

    private connect(): void {
        this.socket = new WebSocket(this.url);

        this.socket.onopen = () => {
            this.isConnected = true;
            this.reconnectAttempts = 0;
            this.startHeartbeat();
            this.flushMessageQueue();
            this.emit('open');
        };

        this.socket.onmessage = (event: MessageEvent) => {
            try {
                const data = JSON.parse(event.data);
                this.emit('message', data);
            } catch (error) {
                this.emit('error', new Error('Failed to parse message data'));
            }
        };

        this.socket.onclose = () => {
            this.isConnected = false;
            this.stopHeartbeat();
            this.emit('close');
            this.attemptReconnect();
        };

        this.socket.onerror = (error: Event) => {
            this.emit('error', error);
        };
    }

    private startHeartbeat(): void {
        this.stopHeartbeat();
        this.heartbeatTimer = setInterval(() => {
            if (this.isConnected) {
                this.send({type: 'ping', timestamp: Date.now()});
            }
        }, this.heartbeatInterval);
    }

    private stopHeartbeat(): void {
        if (this.heartbeatTimer) {
            clearInterval(this.heartbeatTimer);
            this.heartbeatTimer = null;
        }
    }

    private attemptReconnect(): void {
        if (this.reconnectAttempts < this.maxReconnectAttempts) {
            this.reconnectAttempts++;
            this.reconnectTimer = setTimeout(() => {
                this.emit('reconnecting', this.reconnectAttempts);
                this.connect();
            }, this.reconnectDelay);
        } else {
            this.emit('reconnect_failed');
        }
    }

    private flushMessageQueue(): void {
        while (this.messageQueue.length > 0 && this.isConnected) {
            const message = this.messageQueue.shift();
            this.send(message);
        }
    }

    public send(data: any): void {
        if (!this.isConnected) {
            this.messageQueue.push(data);
            return;
        }

        try {
            // data.sign = buildParamsSignStr(data, true);
            this.socket?.send(JSON.stringify(data));
        } catch (error) {
            this.emit('error', error);
        }
    }

    public on(event: string, callback: Function): void {
        if (!this.eventListeners[event]) {
            this.eventListeners[event] = [];
        }
        this.eventListeners[event].push(callback);
    }

    public off(event: string, callback?: Function): void {
        if (!this.eventListeners[event]) return;

        if (callback) {
            this.eventListeners[event] = this.eventListeners[event].filter(
                (cb) => cb !== callback
            );
        } else {
            delete this.eventListeners[event];
        }
    }

    private emit(event: string, ...args: any[]): void {
        if (this.eventListeners[event]) {
            this.eventListeners[event].forEach((callback) => callback(...args));
        }
    }

    public close(): void {
        if (this.reconnectTimer) {
            clearTimeout(this.reconnectTimer);
        }
        this.stopHeartbeat();
        this.socket?.close();
        this.isConnected = false;
    }

    public get connectionStatus(): boolean {
        return this.isConnected;
    }
}

export default WebSocketWrapper;
